/**
 * @file    mjpeg_camera.cpp
 * @brief   mjpeg_camera功能类函数定义
 * @author  Haozhong Xu
 * @date    2025-05-27
 * @version 1.0
 * 
 * @note    类函数实现
 */
#include "camera/mjpeg_camera.hpp"
#include "utils/log.hpp"

#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <cstdio>
#include <stdexcept>
#include <fstream>
#include <string.h>

void MJPEGCamera::Init()
{
    if (!InitDevice())      throw std::runtime_error("打开摄像头失败");
    
    if (!InitFormat())      throw std::runtime_error("设置采集格式失败");

    if (!InitMmap())        throw std::runtime_error("缓存区映射失败");

    if(!StartStreaming())   throw std::runtime_error("打开视频流失败");

}

void MJPEGCamera::Close()
{
    // 关闭视频流
    if (!StopStreaming())  throw std::runtime_error("停止视频流失败");
    // 关闭文件描述符
    if (_fd > 0) {
        close(_fd);
        _fd = -1;
    }
    std::cout<<"mjpeg摄像头关闭"<<std::endl;
}


MJPEGCamera::~MJPEGCamera()
{
    Close();
}


bool MJPEGCamera::InitDevice()
{
    _fd = open(_devicepath.c_str(), O_RDWR);
    if (_fd < 0) {
        perror("打开设备失败");
        return false;
    }

    struct v4l2_capability vcap;
    if (ioctl(_fd, VIDIOC_QUERYCAP, &vcap) < 0 || 
        !(vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
            perror("设备不支持视频采集");
            return false;
        }

        return true;
}

bool MJPEGCamera::InitFormat()
{
    struct v4l2_format fmt{};
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = _width;
    fmt.fmt.pix.height = _height;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    // 设置格式
    if (ioctl(_fd, VIDIOC_S_FMT, &fmt) < 0) {
        perror("设置格式失败");
        return false;
    }
    // 立刻读取格式
    if (ioctl(_fd, VIDIOC_G_FMT, &fmt) < 0) {
        perror("设置格式失败");
        return false;
    }

    LOG_DEBUG("实际分辨率:{}x{}", fmt.fmt.pix.width, fmt.fmt.pix.height);
    LOG_DEBUG("实际像素格式:{}", fmt.fmt.pix.pixelformat);
    LOG_DEBUG("每一帧大小:{} 字节", fmt.fmt.pix.sizeimage);
    return true;
}

// 分配内核缓冲区
bool MJPEGCamera::InitMmap()
{
    struct v4l2_requestbuffers req{};
    req.count = 4;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;

    // 请求内核分配缓冲区
    if (ioctl(_fd, VIDIOC_REQBUFS,&req) < 0) {
        perror("请求缓冲区失败");
        return false;
    }

    for (size_t i = 0; i < req.count; i++) {
        struct v4l2_buffer buf{};
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = i;

        if (ioctl(_fd, VIDIOC_QUERYBUF, &buf) < 0) {
            perror("查询缓冲区失败");
            return false;
        }

        _mmapSize[i] = buf.length;
        _mmapPtr[i] = mmap(nullptr, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, buf.m.offset);
        if (_mmapPtr[i] == MAP_FAILED) {
            perror("内存映射失败");
            return false;
        }

        // 缓冲区放入队列
        if (ioctl(_fd, VIDIOC_QBUF, &buf) < 0) {
            perror("放入队列失败");
            return false;
        }

    }
    return true;
}


// 开始采集视频流
bool MJPEGCamera::StartStreaming()
{
    if (_streaming) {
        return true;
    }

    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(_fd, VIDIOC_STREAMON, &type) < 0) {
        perror("启动视频流失败");
        return false;
    }

    _streaming = true;
    return true;
}

// 停止采集视频流
bool MJPEGCamera::StopStreaming()
{
    if (!_streaming) {
        return true;
    }

    int type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(_fd, VIDIOC_STREAMOFF, &type) < 0) {
        perror("停止视频流失败");
        return false;
    }

    _streaming = false;

    // 取消映射
    for (size_t i = 0; i < 4; ++i) {
        if (_mmapPtr[i]) {
            munmap(_mmapPtr[i], _mmapSize[i]);
            _mmapPtr[i] = nullptr;
            _mmapSize[i] = 0;
        }
    }
    return true;

}


// 采集一帧JPEG图片
bool MJPEGCamera::CaptureOneJPEGFrame(const std::string &filename)
{
    if (!_streaming) {
        std::cerr << "采集未开始，无法抓取帧" << std::endl;
        return false;
    }

    struct v4l2_buffer buf{};
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;

    // 1. 驱动队列中取出一个填充完毕的缓冲区
    if (ioctl(_fd, VIDIOC_DQBUF, &buf) < 0) {
        perror("队列中取出一帧失败");
        return false;
    }

    // 2. 获取MJPEG数据指针和大小
    void *data = _mmapPtr[buf.index];
    size_t size = buf.bytesused;

    // 3. 写入为 JPEG 文件
    std::ofstream outFile(filename, std::ios::binary);
    if (!outFile) {
        std::cerr << "输出文件夹不存在" << filename << std::endl;
        return false;
    }
    outFile.write(reinterpret_cast<char*>(data), size);
    outFile.close();

    LOG_DEBUG("已保存 JPEG 图像:{}, ({}字节)", filename, size); 

    // 4. 将缓冲区重新放回队列
    if (ioctl(_fd, VIDIOC_QBUF, &buf) < 0) {
        perror("VIDIOC_QBUF 失败");
        return false;
    }

    return true;
}



bool MJPEGCamera::UpdateFrame()
{
    if (!_streaming) {
        std::cerr << "采集未开始，无法更新帧" << std::endl;
        return false;
    }

    struct v4l2_buffer buf{};
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;

    // 1. 获取填充好的缓冲区
    if (ioctl(_fd, VIDIOC_DQBUF, &buf) < 0) {
        perror("VIDIOC_DQBUF 失败");
        return false;
    }

    void *data = _mmapPtr[buf.index];
    size_t size = buf.bytesused;

    if (data == nullptr || size == 0) {
        std::cerr << "帧数据无效" << std::endl;
        return false;
    }

    // 2. 拷贝数据到 _jpegData
    _jpegData.resize(size);
    memcpy(_jpegData.data(), data, size);
    std::cout << "[Camera] 采集到一帧 MJPEG，大小: " << size << " 字节" << std::endl;

    // 3. 归还缓冲区
    if (ioctl(_fd, VIDIOC_QBUF, &buf) < 0) {
        perror("VIDIOC_QBUF 失败");
        return false;
    }

    return true;
}

const std::vector<uint8_t>& MJPEGCamera::GetFrame() const 
{
    return _jpegData;
}
